﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web;
using MVCQuick.Framework.Logging;
using NH = NHibernate;
using NHibernate.Mapping.ByCode;


namespace MVCQuick.Framework.Repository.NHibernate
{
    public static class NHibernateManager
    {
        private const string CurrentSessionKey = "nhibernate.current_session";

        private static Logger logger;
        private static NH.Cfg.Configuration configuration;
        private static NH.ISessionFactory sessionFactory;     
        private static string connectionString;
        private static string providerName;

        public static NH.ISessionFactory SessionFactory
        {
            get
            {
                if (sessionFactory == null)
                {
                    sessionFactory = configuration.BuildSessionFactory();
                }
                return sessionFactory;
            }
        }

        static NHibernateManager()
        {

        }

        public static void Configuration()
        {


            logger = Logger.GetLogger(typeof(NHibernateManager));

            configuration = new NH.Cfg.Configuration();

            if (System.Configuration.ConfigurationManager.ConnectionStrings.Count > 0)
            {
                System.Configuration.ConnectionStringSettings settings =
                    System.Configuration.ConfigurationManager.ConnectionStrings[0];

                connectionString = settings.ConnectionString;
                providerName = settings.ProviderName;
            }

            string dialect;
            string driver;
            GetDialectAndDriver(providerName, out dialect, out driver);

            configuration.SetProperty("connection.provider", "NHibernate.Connection.DriverConnectionProvider");
            configuration.SetProperty("dialect", dialect);
            configuration.SetProperty("connection.driver_class", driver);           
            configuration.SetProperty("connection.connection_string", connectionString);
            configuration.SetProperty("connection.release_mode", "on_close");
            configuration.SetProperty("show_sql", "true");  
        }

        public static void AddMapping(string appName, params Assembly[] assemblies)
        {
            NH.Mapping.ByCode.ModelMapper mapper = new NH.Mapping.ByCode.ModelMapper(new EntityModelInspector());
            mapper.AddEntityMappings(appName + "_", assemblies);
            var hbmMappings = mapper.CompileMappingForAllExplicitlyAddedEntities();
            logger.Info(hbmMappings.AsString());
            configuration.AddDeserializedMapping(hbmMappings, appName + "Mappings");
        }      

        public static void UpdateSchema()
        {

            NH.Tool.hbm2ddl.SchemaUpdate su = new NH.Tool.hbm2ddl.SchemaUpdate(configuration);
            su.Execute(true, true);
        }

        public static NH.ISession GetCurrentSession()
        {
            HttpContext context = HttpContext.Current;
            NH.ISession currentSession = context.Items[CurrentSessionKey] as NH.ISession;

            if (currentSession == null)
            {
                currentSession = SessionFactory.OpenSession();
                context.Items[CurrentSessionKey] = currentSession;
            }

            return currentSession;
        }

        public static void CloseSession()
        {
            HttpContext context = HttpContext.Current;
            NH.ISession currentSession = context.Items[CurrentSessionKey] as NH.ISession;

            if (currentSession == null)
            {               
                return;
            }

            currentSession.Close();
            context.Items.Remove(CurrentSessionKey);
        }

        public static void CloseSessionFactory()
        {
            if (sessionFactory != null)
            {
                sessionFactory.Close();
            }
        }


        private static void GetDialectAndDriver(string providerName, out string dialect, out string driver)
        {
            var objects = new[]
           {
               new {Name="DB2", Dialect="NHibernate.Dialect.DB2Dialect", Driver="NHibernate.Driver.DB2Driver"},
               new {Name="DB2400", Dialect="NHibernate.Dialect.DB2400Dialect", Driver="NHibernate.Driver.DB2400Driver"},

               new {Name="Firebird", Dialect="NHibernate.Dialect.FirebirdDialect", Driver="NHibernate.Driver.FirebirdDriver"},
               
               new {Name="MsSql2000", Dialect="NHibernate.Dialect.MsSql2000Dialect", Driver="NHibernate.Driver.SqlClientDriver"},
               new {Name="MsSql2005", Dialect="NHibernate.Dialect.MsSql2005Dialect", Driver="NHibernate.Driver.SqlClientDriver"},
               new {Name="MsSql2008", Dialect="NHibernate.Dialect.MsSql2008Dialect", Driver="NHibernate.Driver.Sql2008Driver"},
               new {Name="MsSqlCe", Dialect="NHibernate.Dialect.MsSqlCeDialect", Driver="NHibernate.Driver.SqlServerCeDriver"},

               new {Name="MySQL", Dialect="NHibernate.Dialect.MySQLDialect", Driver="NHibernate.Driver.MySqlDataDriver"},
               new {Name="MySQL5", Dialect="NHibernate.Dialect.MySQLDialect", Driver="NHibernate.Driver.MySqlDataDriver"},

               new {Name="Oracle8i", Dialect="NHibernate.Dialect.Oracle8iDialect", Driver="NHibernate.Driver.OracleClientDriver"},
               new {Name="Oracle9i", Dialect="NHibernate.Dialect.Oracle9iDialect", Driver="NHibernate.Driver.OracleClientDriver"},
               new {Name="Oracle10g", Dialect="NHibernate.Dialect.Oracle10gDialect", Driver="NHibernate.Driver.OracleClientDriver"},

               new {Name="PostgreSQL", Dialect="NHibernate.Dialect.PostgreSQLDialect", Driver="NHibernate.Driver.NpgsqlDriver"},
               new {Name="PostgreSQL81", Dialect="NHibernate.Dialect.PostgreSQL81Dialect", Driver="NHibernate.Driver.NpgsqlDriver"},
               new {Name="PostgreSQL82", Dialect="NHibernate.Dialect.PostgreSQL82Dialect", Driver="NHibernate.Driver.NpgsqlDriver"},
          
               new {Name="SQLite", Dialect="NHibernate.Dialect.SQLiteDialect", Driver="NHibernate.Driver.SQLite20Driver"}
           };

            var o = objects.Single(x => x.Name == providerName);

            if (o != null)
            {
                dialect = o.Dialect;
                driver = o.Driver;

                return;
            }

            dialect = String.Empty;
            driver = String.Empty;
            return;
        }
    }
}